﻿package we3d.renderer.realistic 
{
	import we3d.renderer.ClipUtil;
	import we3d.View3d;
	import we3d.core.Camera3d;
	import we3d.scene.SceneObject;
	import we3d.mesh.Face;
	import we3d.mesh.Vertex;
	import we3d.mesh.UVCoord;
	import we3d.math.Matrix3d;
	import we3d.math.Plane;
	import we3d.renderer.IRenderer
	
	/**
	* Polygon clipping against the five frustum planes of the camera (left, top, right, bottom and near).
	*/
	public class ClipFrustum extends ClipUtil implements IRenderer 
	{
		public function ClipFrustum () {}
		
		public function draw (view:View3d) :void {
			
			var f:Number = view.scene.currentFrame;
			var v:Camera3d = view.scene.cam;
			var objectList:Array = view.scene.objectList;
			var objs:int = objectList.length;
			var cgv:Matrix3d = new Matrix3d();
			var farPlane:Number = v._farClipping;
			
			view.initFrame(f);
			
			plw = v._nearClipping == 0 ? .0005 : v._nearClipping;
			var vt:Number = v.t;
			var vs:Number = v.s;
			
			var p:Face;
			var o:SceneObject;
			var i:int;
			var j:int;
			var L:int;
			var r:Array;
			var clp1:Boolean;
			var clp2:Boolean;
			var pL:int;
			var vtxs:Array;
			var k:int;
			var vtxb:Vertex;
			var splitpts:Array;
			var splitpts2:Array;
			var vtxa:Vertex;
			var uvs:Array;
			var splituvs:Array;
			var splituvs2:Array 
			var u:UVCoord;
			var u0:UVCoord;
			var splitface:Face;
			var e:Boolean;
			
			var x:Number;	var y:Number;	var z:Number; var mz:Number;
			var ofc:int;
			var cgva:Number;	var cgvb:Number;	var cgvc:Number;
			var cgve:Number;	var cgvf:Number;	var cgvg:Number;
			var cgvi:Number;	var cgvj:Number;	var cgvk:Number;
			var cgvm:Number;	var cgvn:Number;	var cgvo:Number;
			var gv:Matrix3d;
			
			var camx:Number = v.transform.gv.m;
			var camy:Number = v.transform.gv.n;
			var camz:Number = v.transform.gv.o;
			
			for(i=0; i<objs; i++) {
				
				o = objectList[i];
				if(o.initFrame(f, v, cgv)) continue;
				if(o.initMesh(view, cgv)) continue;
				
				r = o.polygons;
				L = r.length;
				
				ofc = o.frameCounter;
				
				gv = o.transform.gv;
				
				cgva = cgv.a;	cgvb = cgv.b;	cgvc = cgv.c;
				cgve = cgv.e;	cgvf = cgv.f;	cgvg = cgv.g;
				cgvi = cgv.i;	cgvj = cgv.j;	cgvk = cgv.k;
				cgvm = cgv.m;	cgvn = cgv.n;	cgvo = cgv.o + plw;
				
				for(j=0; j<L; j++) {
					
					p = r[j];
					pL = p.vLen;
					if(pL < 2) continue;
					
					vtxb = p.normal;
					vtxb.wz = gv.c * vtxb.x + gv.g * vtxb.y + gv.k * vtxb.z;
					vtxb.wy = gv.b * vtxb.x + gv.f * vtxb.y + gv.j * vtxb.z;
					vtxb.wx = gv.a * vtxb.x + gv.e * vtxb.y + gv.i * vtxb.z;
					
					vtxa = p.a;
					
					if(p.surface.hideBackfaces) {
						
						z = gv.c * vtxa.x + gv.g * vtxa.y + gv.k * vtxa.z + gv.o - camz;
						y = gv.b * vtxa.x + gv.f * vtxa.y + gv.j * vtxa.z + gv.n - camy;
						x = gv.a * vtxa.x + gv.e * vtxa.y + gv.i * vtxa.z + gv.m - camx;
					
						if(x*vtxb.wx + y*vtxb.wy + z*vtxb.wz < 0) continue;
					}
					
					vtxs = p.vtxs;
					
					for(k=0; k<pL; k++) {
						vtxb = vtxs[k];
						if(vtxb.frameCounter1 != ofc) {
							vtxb.wz = cgvc * vtxb.x + cgvg * vtxb.y + cgvk * vtxb.z + cgvo;
							vtxb.wy = cgvb * vtxb.x + cgvf * vtxb.y + cgvj * vtxb.z + cgvn;
							vtxb.wx = cgva * vtxb.x + cgve * vtxb.y + cgvi * vtxb.z + cgvm;
							vtxb.frameCounter1 = ofc;
						}
					}
					
					z = vtxa.wz;
					
					if(z < plw) {
						e = true;
						for(k=1; k<pL; k++) {
							vtxb = vtxs[k];
							if(vtxb.wz >= plw) {
								e = false;
								break;
							}
						}
						if(e) continue;
					}
					else if(z > farPlane) {
						e = true;
						for(k=1; k<pL; k++) {
							vtxb = vtxs[k];
							if(vtxb.wz <= farPlane) {
								e = false;
								break;
							}
						}
						if(e) continue;
					}
					
					if(vtxa.wy < -z) {
						e = true;
						for(k=1; k<pL; k++) {
							vtxb = vtxs[k];
							if(vtxb.wy >= -vtxb.wz) {
								e = false;
								break;
							}
						}
						if(e) continue;
					}
					else if(vtxa.wy > z) {
						e = true;
						for(k=1; k<pL; k++) {
							vtxb = vtxs[k];
							if(vtxb.wy <= vtxb.wz) {
								e = false;
								break;
							}
						}
						if(e) continue;
					}
					
					if(vtxa.wx < -z) {
						e = true;
						for(k=1; k<pL; k++) {
							vtxb = vtxs[k];
							if(vtxb.wx >= -vtxb.wz) {
								e = false;
								break;
							}
						}
						if(e) continue;
					}
					else if(vtxa.wx > z) {
						e = true;
						for(k=1; k<pL; k++) {
							vtxb = vtxs[k];
							if(vtxb.wx <= vtxb.wz) {
								e = false;
								break;
							}
						}
						if(e) continue;
					}
					
					e = true;
					for(k=0; k<pL; k++) {
						vtxb = vtxs[k];
						z = vtxb.wz;
						if(vtxb.wy > z || vtxb.wy < -z || vtxb.wx > z || vtxb.wx < -z || z < plw || z > farPlane) { // point culled
							e = false;
							break;
						}
					}
					if(e) {
						for(k=0; k<pL; k++) {
							vtxb = vtxs[k];
							if(vtxb.frameCounter2 != ofc) {
								vtxb.sx = vt + vtxb.wx/vtxb.wz * vt;
								vtxb.sy = vs - vtxb.wy/vtxb.wz * vs;
								vtxb.frameCounter2 = ofc;
							}
						}
						o.polyRenderer.drawPoly(view, p);
						continue;
					}
					
					splitpts = [];
					
					if(p.uvs == null) {
						// clip top plane ///////////////////////////
						vtxa = vtxs[0];
						clp1 = vtxa.wy > vtxa.wz;
						for(k=1; k<pL; k++) {
							
							vtxb = vtxs[k];
							clp2 = vtxb.wy > vtxb.wz;
							
							if(!clp1) splitpts.push( vtxa );
							if(clp1 != clp2) splitpts.push( splitVertexPlane(vtxa, vtxb, topPlane) );
							
							clp1 = clp2;
							vtxa = vtxb;
						}
						vtxb = vtxs[0];
						clp2 = vtxb.wy > vtxb.wz;
						if(!clp1) splitpts.push( vtxa );
						if(clp1 != clp2) splitpts.push( splitVertexPlane(vtxa, vtxb, topPlane) );
						
						
						
						pL = splitpts.length;
						if(pL < 2) continue;
						
						// clip bottom plane ///////////////////////////
						splitpts2 = [];
						vtxa = splitpts[0];
						clp1 = vtxa.wy < -vtxa.wz;
						for(k=1; k<pL; k++) {
							
							vtxb = splitpts[k];
							clp2 = vtxb.wy < -vtxb.wz;
							
							if(!clp1) splitpts2.push( vtxa );
							if(clp1 != clp2) splitpts2.push( splitVertexPlane(vtxa, vtxb, bottomPlane) );
							
							clp1 = clp2;
							vtxa = vtxb;
						}
						vtxb = splitpts[0];
						clp2 = vtxb.wy < -vtxb.wz;
						if(!clp1) splitpts2.push( vtxa );
						if(clp1 != clp2) splitpts2.push( splitVertexPlane(vtxa, vtxb, bottomPlane) );
						
						
						pL = splitpts2.length;
						if(pL < 2) continue;
						
						// clip left plane ///////////////////////////
						splitpts = [];
						vtxa = splitpts2[0];
						clp1 = vtxa.wx < -vtxa.wz;
						for(k=1; k<pL; k++) {
							
							vtxb = splitpts2[k];
							clp2 = vtxb.wx < -vtxb.wz;
							
							if(!clp1) splitpts.push( vtxa );
							if(clp1 != clp2) splitpts.push( splitVertexPlane(vtxa, vtxb, leftPlane) );
							
							clp1 = clp2;
							vtxa = vtxb;
						}
						vtxb = splitpts2[0];
						clp2 = vtxb.wx < -vtxb.wz;
						if(!clp1) splitpts.push( vtxa );
						if(clp1 != clp2) splitpts.push( splitVertexPlane(vtxa, vtxb, leftPlane) );
						
						
						
							
						pL = splitpts.length;
						if(pL < 2) continue;
						
						// clip right plane ///////////////////////////
						splitpts2 = [];
						vtxa = splitpts[0];
						clp1 = vtxa.wx > vtxa.wz;
						for(k=1; k<pL; k++) {
							
							vtxb = splitpts[k];
							clp2 = vtxb.wx > vtxb.wz;
							
							if(!clp1) splitpts2.push( vtxa );
							if(clp1 != clp2) splitpts2.push( splitVertexPlane(vtxa, vtxb, rightPlane) );
							
							clp1 = clp2;
							vtxa = vtxb;
						}
						vtxb = splitpts[0];
						clp2 = vtxb.wx > vtxb.wz;
						if(!clp1) splitpts2.push( vtxa );
						if(clp1 != clp2) splitpts2.push( splitVertexPlane(vtxa, vtxb, rightPlane) );
						
						
						
						pL = splitpts2.length;
						if(pL < 2) continue;
						
						// clip near plane ///////////////////////////
						splitpts = [];
						vtxa = splitpts2[0];
						clp1 = vtxa.wz < plw;
						for(k=1; k<pL; k++) {
							
							vtxb = splitpts2[k];
							clp2 = vtxb.wz < plw;
							
							if(!clp1) splitpts.push( vtxa );
							if(clp1 != clp2) splitpts.push( splitVertex(vtxb, vtxa) );
							
							clp1 = clp2;
							vtxa = vtxb;
						}
						vtxb = splitpts2[0];
						clp2 = vtxb.wz < v._nearClipping;
						if(!clp1) splitpts.push( vtxa );
						if(clp1 != clp2) splitpts.push( splitVertex(vtxb, vtxa) );
						
						pL = splitpts.length;
						
						if(pL> 1) {
							
							splitface = new Face();
							splitface.surface = p.surface;
							splitface.vLen = pL;
							splitface.vtxs = splitpts;
							splitface.a = splitpts[0];
							splitface.b = splitpts[1];
							splitface.c = splitpts[2];
							splitface.ax = p.ax;
							splitface.ay = p.ay;
							splitface.az = p.az;
							splitface.normal.wx = p.normal.wx;
							splitface.normal.wy = p.normal.wy;
							splitface.normal.wz = p.normal.wz;
							splitface.sortFar = p.sortFar;
							splitface.so = p.so;
							
							for(k=0; k<pL; k++) {
								vtxb = splitpts[k];
								if(vtxb.frameCounter2 != ofc) {
									vtxb.sx = vt + vtxb.wx/vtxb.wz * vt;
									vtxb.sy = vs - vtxb.wy/vtxb.wz * vs;
									vtxb.frameCounter2 = ofc;
								}
							}
							o.polyRenderer.drawPoly(view, splitface);
						}
						
					}
					else {
						
						// clip with uv
						uvs = p.uvs;
						
						splituvs = [];
						
						// clip top plane ///////////////////////////
						vtxa = vtxs[0];
						u0 = uvs[0];
						clp1 = vtxa.wy > vtxa.wz;
						
						for(k=1; k<pL; k++) {
							
							vtxb = vtxs[k];
							u = uvs[k];
							
							clp2 = vtxb.wy > vtxb.wz;
							
							if(!clp1) {
								splitpts.push( vtxa );
								splituvs.push( u0 );
							}
							if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, topPlane, u0, u, splitpts, splituvs);
							
							clp1 = clp2;
							vtxa = vtxb;
							u0 = u;
						}
						
						vtxb = vtxs[0];
						u = uvs[0];
						clp2 = vtxb.wy > vtxb.wz;
						if(!clp1) {
							splitpts.push( vtxa );
							splituvs.push( u0 );
						}
						if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, topPlane, u0, u, splitpts, splituvs);
						
						
						
						pL = splitpts.length;
						if(pL < 2) continue;
						
						// clip bottom plane ///////////////////////////
						splitpts2 = [];
						splituvs2 = [];
						
						vtxa = splitpts[0];
						u0 = splituvs[0];
						clp1 = vtxa.wy < -vtxa.wz;
						for(k=1; k<pL; k++) {
							
							vtxb = splitpts[k];
							u = splituvs[k];
							
							clp2 = vtxb.wy < -vtxb.wz;
							
							if(!clp1) {
								splitpts2.push( vtxa );
								splituvs2.push( u0 );
							}
							if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, bottomPlane, u0, u, splitpts2, splituvs2);
							
							clp1 = clp2;
							vtxa = vtxb;
							u0 = u;
						}
						
						vtxb = splitpts[0];
						u = splituvs[0];
						clp2 = vtxb.wy < -vtxb.wz;
						if(!clp1) {
							splitpts2.push( vtxa );
							splituvs2.push( u0 );
						}
						if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, bottomPlane, u0, u, splitpts2, splituvs2);
						
						
						pL = splitpts2.length;
						if(pL < 2) continue;
						
						// clip left plane ///////////////////////////
						splitpts = [];
						splituvs = [];
						vtxa = splitpts2[0];
						u0 = splituvs2[0];
						clp1 = vtxa.wx < -vtxa.wz;
						
						for(k=1; k<pL; k++) {
							
							vtxb = splitpts2[k];
							u = splituvs2[k];
							clp2 = vtxb.wx < -vtxb.wz;
							
							if(!clp1) {
								splitpts.push( vtxa );
								splituvs.push( u0 );
							}
							if(clp1 != clp2) {
								splitVertexUVPlane(vtxa, vtxb, leftPlane, u0, u, splitpts, splituvs);
							}
							
							clp1 = clp2;
							vtxa = vtxb;
							u0 = u;
						}
						vtxb = splitpts2[0];
						u = splituvs2[0];
						clp2 = vtxb.wx < -vtxb.wz;
						if(!clp1) {
							splitpts.push( vtxa );
							splituvs.push( u0 );
						}
						if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, leftPlane, u0, u, splitpts, splituvs);
						
						
						pL = splitpts.length;
						if(pL < 2) continue;
						
						// clip right plane ///////////////////////////
						splitpts2 = [];
						splituvs2 = [];
						vtxa = splitpts[0];
						u0 = splituvs[0];
						clp1 = vtxa.wx > vtxa.wz;
						for(k=1; k<pL; k++) {
							
							vtxb = splitpts[k];
							u = splituvs[k];
							clp2 = vtxb.wx > vtxb.wz;
							
							if(!clp1) {
								splitpts2.push( vtxa );
								splituvs2.push( u0 );
							}
							if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, rightPlane, u0, u, splitpts2, splituvs2);
							
							clp1 = clp2;
							vtxa = vtxb;
							u0 = u;
						}
						vtxb = splitpts[0];
						u = splituvs[0];
						clp2 = vtxb.wx > vtxb.wz;
						if(!clp1) {
							splitpts2.push( vtxa );
							splituvs2.push( u0 );
						}
						if(clp1 != clp2) splitVertexUVPlane(vtxa, vtxb, rightPlane, u0, u, splitpts2, splituvs2);
						
						
						
						pL = splitpts2.length;
						if(pL < 2) continue;
						
						// clip near plane ///////////////////////////
						splitpts = [];
						splituvs = [];
						vtxa = splitpts2[0];
						u0 = splituvs2[0];
						clp1 = vtxa.wz < plw;
						for(k=1; k<pL; k++) {
							
							vtxb = splitpts2[k];
							u = splituvs2[k];
							clp2 = vtxb.wz < plw;
							
							if(!clp1) {
								splitpts.push( vtxa );
								splituvs.push( u0 );
							}
							if(clp1 != clp2)  splitVertexUV(vtxb, vtxa, u, u0, splitpts, splituvs);
							
							clp1 = clp2;
							vtxa = vtxb;
							u0 = u;
						}
						vtxb = splitpts2[0];
						u = splituvs2[0];
						clp2 = vtxb.wz < v._nearClipping;
						if(!clp1) {
							splitpts.push( vtxa );
							splituvs.push( u0 );
						}
						if(clp1 != clp2) splitVertexUV(vtxb, vtxa, u, u0, splitpts, splituvs);
						
						pL = splitpts.length;
						
						if(pL > 1) {
							
							splitface = new Face();
							splitface.surface = p.surface;
							splitface.vLen = pL;
							splitface.vtxs = splitpts;
							splitface.a = splitpts[0];
							splitface.b = splitpts[1];
							splitface.c = splitpts[2];
							splitface.ax = p.ax;
							splitface.ay = p.ay;
							splitface.az = p.az;
							splitface.normal.wx = p.normal.wx;
							splitface.normal.wy = p.normal.wy;
							splitface.normal.wz = p.normal.wz;
							splitface.sortFar = p.sortFar;
							splitface.so = p.so;
							splitface.uvs = splituvs;
							p.surface.initFace(splitface);
							
							for(k=0; k<pL; k++) {
								vtxb = splitpts[k];
								if(vtxb.frameCounter2 != ofc) {
									vtxb.sx = vt + vtxb.wx/vtxb.wz * vt;
									vtxb.sy = vs - vtxb.wy/vtxb.wz * vs;
									vtxb.frameCounter2 = ofc;
								}
							}
							
							o.polyRenderer.drawPoly(view, splitface);
						}
						
					}
					
				}
			}
			
			view.endFrame(f);
		}
	}
}
